package connectfour;

public class AIPlayer extends Player {

    int myPlayerId;
    int searchDepth;
    java.util.Random rnd = new java.util.Random();
    GUI gui;
    int showDepth;

    public AIPlayer(int myPlayerId, int searchDepth, GUI gui, int showDepth) {
        this.myPlayerId = myPlayerId;
        this.searchDepth = searchDepth;
        this.gui = gui;
        this.showDepth = showDepth;
    }

    public int getNextMove(Board board) {
        int bestSlot = -1;
        double bestDist = 2;
        System.out.print("AI Options: ");

        Board tmpBoard = new Board(board);
        int i = (board.getNumCols() - 1) / 2;
        for (int jump = 0; jump < board.getNumCols(); jump++) {
            if (jump % 2 == 0) {
                i -= jump;
            } else {
                i += jump;
            }
            if (tmpBoard.doMove(myPlayerId, i)) {

                //double actual = minmax(tmpBoard,enemy(myPlayerId),searchDepth -1);
                double actual;
                if (myPlayerId == 1) {
                    actual = abMin(tmpBoard, searchDepth - 1, -10, 10);
                } else {
                    actual = abMax(tmpBoard, searchDepth - 1, -10, 10);
                }

                tmpBoard.undoMove();
                System.out.print("[" + i + "|" + actual + "] ");

                double testDist = Math.abs(actual - myPlayerId);
                if (testDist == bestDist && rnd.nextBoolean()) {
                    bestDist = testDist;
                    bestSlot = i;
                } else if (testDist < bestDist) {
                    bestDist = testDist;
                    bestSlot = i;
                }
            }
        }
        System.out.println("");
        if (bestSlot < 0) {
            System.out.println("this should never happen, there are no possible moves");
            System.exit(1);
        }
        return bestSlot;

    }

    //@Override
    public int getNextMove(Board board, boolean a) {
        int bestSlot = -1;
        System.out.print("AI Options: ");
        Board tmpBoard = new Board(board);
        int i = (board.getNumCols() - 1) / 2;
        double alpha = -10;
        double beta = 10;
        if (myPlayerId == 0) {
            double minScore = beta;
            for (int jump = 0; jump < tmpBoard.getNumCols(); jump++) {
                if (jump % 2 == 0) {
                    i -= jump;
                } else {
                    i += jump;
                }
                if (tmpBoard.doMove(0, i)) {
                    double val = abMax(tmpBoard, searchDepth - 1, alpha, beta);
                    tmpBoard.undoMove();
                    System.out.print("[" + i + "|" + val + "] ");
                    if (val < minScore) {
                        bestSlot = i;
                        minScore = val;
                    }
                    //beta = minScore;
                }
            }
        } else {
            double maxScore = alpha;
            for (int jump = 0; jump < tmpBoard.getNumCols(); jump++) {
                if (jump % 2 == 0) {
                    i -= jump;
                } else {
                    i += jump;
                }
                if (tmpBoard.doMove(0, i)) {
                    double val = abMin(tmpBoard, searchDepth - 1, alpha, beta);
                    tmpBoard.undoMove();
                    System.out.print("[" + i + "|" + val + "] ");
                    if (val > maxScore) {
                        bestSlot = i;
                        maxScore = val;
                    }
                    //alpha = maxScore;
                }
            }
        }
        System.out.println("");
        if (bestSlot < 0) {
            System.out.println("this should never happen, there are no possible moves");
            System.exit(1);
        }
        return bestSlot;
    }
    // alpha beta min
    private double abMin(Board board, int depth, double alpha, double beta) {
        int winner = board.checkWinner();
        if (depth <= 0 || board.isFull() || winner != -1) {
            if (winner == 0 || winner == 1) {
                return winner;
            }
            return board.getValue();
        }
        double minScore = beta;
        int i = (board.getNumCols() - 1) / 2;
        for (int jump = 0; jump < board.getNumCols(); jump++) {
            if (jump % 2 == 0) {
                i -= jump;
            } else {
                i += jump;
            }
            if (board.doMove(0, i)) {
                minScore = Math.min(minScore, abMax(board, depth - 1, alpha, beta));
                board.undoMove();
                beta = minScore;
                if (alpha >= beta) {
                    return minScore;
                }
            }
        }
        return minScore;
    }
    // alpha beta max
    private double abMax(Board board, int depth, double alpha, double beta) {
        int winner = board.checkWinner();
        if (depth <= 0 || board.isFull() || winner != -1) {
            if (winner == 0 || winner == 1) {
                return winner;
            }
            return board.getValue();
        }
        double maxScore = alpha;
        int i = (board.getNumCols() - 1) / 2;
        for (int jump = 0; jump < board.getNumCols(); jump++) {
            if (jump % 2 == 0) {
                i -= jump;
            } else {
                i += jump;
            }
            if (board.doMove(1, i)) {
                maxScore = Math.max(maxScore, abMin(board, depth - 1, alpha, beta));
                board.undoMove();
                alpha = maxScore;
                if (alpha >= beta) {
                    return maxScore;
                }
            }
        }
        return maxScore;
    }
    //simple minmax tree
    private double minmax(Board board, int playerId, int depth) {
        int winner = board.checkWinner();
        if (depth <= 0 || board.isFull() || winner != -1) {
            if (winner == 0 || winner == 1) {
                return winner;
            }
            return board.getValue();
        }
        double bestDist = 2;
        double bestActual = -1;
        int i = (board.getNumCols() - 1) / 2;
        for (int jump = 0; jump < board.getNumCols(); jump++) {
            if (jump % 2 == 0) {
                i -= jump;
            } else {
                i += jump;
            }
            if (board.doMove(playerId, i)) {
                double actual = minmax(board, enemy(playerId), depth - 1);
                board.undoMove();
                double testDist = Math.abs(actual - playerId);
                if (testDist == 0) {
                    return actual;
                }
                if (testDist == bestDist && rnd.nextBoolean()) {
                    bestDist = testDist;
                    bestActual = actual;
                } else if (testDist < bestDist) {
                    bestDist = testDist;
                    bestActual = actual;
                }
            }
        }
        if (bestActual >= 0 && bestActual <= 1) {
            return bestActual;
        } else {
            System.out.println("VALUE IS OUT OF RANGE!!!");
            return board.getValue();
        }
    }

    private int enemy(int myPlayerId) {
        return (myPlayerId - 1) * -1;
    }
}
